//
//  T3FAMapView.swift
//  t3_map
//
//  Created by Cunhai Li on 2024/10/22.
//

import UIKit
import SnapKit
import AMapNaviKit
import Bundle_Log
import t3_assets

class T3FMAMapView: UIView {

    weak var delegate: (any T3FMapViewDelegate)?

    private let mapView: MAMapView
    private let options: T3FMapOptions

    // MARK: - private
    private var mapDidInit = false {
        didSet {
            showAnnotationTask?()
            showPolylineTask?()
        }
    }

    private var innerAnnotations: [T3FMAAnnotation] = []
    private var innerPolylines: [T3FMAPolyline] = []
    private var innerPolygons: [T3FMAPolygon] = []

    private var showAnnotationTask: (() -> Void)?
    private var showPolylineTask: (() -> Void)?

    init(frame: CGRect, options: T3FMapOptions) {

        /// 初始化高德地图
        let view = MAMapView()
        view.mapType = .standard
        view.backgroundColor = .white
        view.userTrackingMode = .none
        view.showsCompass = false
        view.showsScale = false
        view.showsUserLocation = false
        view.isRotateEnabled = false
        view.isRotateCameraEnabled = false
        view.touchPOIEnabled = false
        view.zoomLevel = T3FMAMapConst.defaultZoomLevel
        view.maxRenderFrame = 60

        let userLoc = MAUserLocationRepresentation()

        userLoc.image = UIImage.t3_assetsImage(name: T3AssetsName.map_icon_user_location.value)
        userLoc.showsHeadingIndicator = true
        userLoc.strokeColor = T3FColorUtil.hex("#3289FA", alpha: 0.35)
        userLoc.fillColor = T3FColorUtil.hex("#3289FA", alpha: 0.15)
        userLoc.lineWidth = 1
        view.update(userLoc)

        if let url = Bundle.main.url(forResource: "T3FMap", withExtension: "bundle"),
           let urlBundle = Bundle(url: url),
           let configFilePath = urlBundle.path(forResource: "style_extra", ofType: "data"),
           let fileData = try? Data(contentsOf: URL(fileURLWithPath: configFilePath)),
           let styleFilePath = urlBundle.path(forResource: "style", ofType: "data"),
           let styleData = try? Data(contentsOf: URL(fileURLWithPath: styleFilePath)) {

            let styleOptions = MAMapCustomStyleOptions()

            styleOptions.styleExtraData = fileData
            styleOptions.styleData = styleData
            view.setCustomMapStyleOptions(styleOptions)
            view.customMapStyleEnabled = true
        }

        self.options = options
        self.mapView = view
        
        /// 设置地图初始化状态
        if let initCameraPosition = options.initCameraPosition {
            let mapStatus = MAMapStatus(center: initCameraPosition.centerCoordinate, zoomLevel: T3FMAMapConst.defaultZoomLevel, rotationDegree: 0, cameraDegree: 0, screenAnchor: CGPoint(x: 0.5, y: 0.5))
            view.setMapStatus(mapStatus, animated: false, duration: 0)
        }

        super.init(frame: frame)
        view.delegate = self
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func willMove(toSuperview newSuperview: UIView?) {

        if let _ = newSuperview {
            
            self.addSubview(mapView)
            mapView.snp.makeConstraints { make in
                make.edges.equalToSuperview()
            }
        }
        else {
            mapView.removeFromSuperview()
        }
    }

    deinit {
        T3Log.debug("高德地图-释放")
    }
}

extension T3FMAMapView: T3FMapView {

    /// 地图的视图锚点
    var screenAnchor: CGPoint {
        get {
            return mapView.screenAnchor
        }
        set {
            mapView.screenAnchor = newValue
        }
    }
    
    /// 地图中心点
    var centerCoordinate: CLLocationCoordinate2D {
        return mapView.centerCoordinate
    }

    var showsUserLocation: Bool {
        get {
            return mapView.showsUserLocation
        }
        set {
            mapView.showsUserLocation = newValue
        }
    }
    
    ///定位用户位置的模式
    func setupUserTrackingMode(mode: T3UserTrackingMode, animated: Bool) {
        let trackMode: MAUserTrackingMode
        switch mode {
        case .none:
            trackMode = .none
        case .follow:
            trackMode = .follow
        case .followWithHeading:
            trackMode = .followWithHeading
        }
        mapView.setUserTrackingMode(trackMode, animated: animated)
    }

    ///当前位置再地图中是否可见
    var userLocationVisible: Bool {
        return mapView.isUserLocationVisible
    }

    func setupMapView(zoomLevel: CGFloat, animated: Bool) {
        mapView.setZoomLevel(zoomLevel, animated: animated)
    }

    func setupCenter(_ coordinate: CLLocationCoordinate2D, animated: Bool) {
        mapView.setCenter(coordinate, animated: animated)
    }

    func setupVisibleMapRect(_ rect: CGRect, edgePadding: UIEdgeInsets, animated: Bool) {
        mapView.setVisibleMapRect(MAMapRect(origin: MAMapPoint(x: rect.origin.x, y: rect.origin.y),
                                            size: MAMapSize(width: rect.size.width, height: rect.size.height)),
                                  edgePadding: edgePadding,
                                  animated: animated)
    }

    func takeSnapshot(completed: @escaping (UIImage?) -> Void) {
        mapView.takeSnapshot(in: mapView.bounds, timeoutInterval: T3FMAMapConst.snapshotTimeout) { img, state in
            let msg = (state == 1) ? "完整" : "载入不完整"
            T3Log.debug("高德地图截图，当前加载-[\(msg)]")
            completed(img)
        }
    }


    func convert(_ point: CGPoint, toCoordinateFrom view: UIView) -> CLLocationCoordinate2D {
        return mapView.convert(point, toCoordinateFrom: view)
    }

    // MARK: - func T3Polyline
    /// 当前地图上的全部线段
    var polylines: [T3FPolyline] {
        return innerPolylines
    }

    func add(polyline options: T3FPolylineOptions) throws -> T3FPolyline {
        var points = options.points
        let realPolyline: MAPolyline
        
        if options.lineType == .multi {
            let indexes: [NSNumber] = options.lineIndexes.map { NSNumber(value: $0) }
            guard let polyline = MAMultiPolyline(coordinates: &points, count: UInt(points.count), drawStyleIndexes: indexes) else {
                throw T3FMAMapError.polylineAddFailed
            }
            realPolyline = polyline
        } else {
            guard let polyline = MAPolyline(coordinates: &points, count: UInt(points.count)) else {
                throw T3FMAMapError.polylineAddFailed
            }
            realPolyline = polyline
        }

        let polyline = T3FMAPolyline(options: options, realPolyline: realPolyline)
        innerPolylines.append(polyline)
        mapView.add(realPolyline)
        return polyline
    }

    /// 更新线段路径信息
    func update(polyline options: T3FPolylineOptions) {
        //根据id找到当前已绘制的polyline
        if let polyline = innerPolylines.first { $0.options.identifier == options.identifier } {
            var coords = options.points
            if polyline.options.lineType == .multi {
                if let realPolyline = polyline.realPolyline as? MAMultiPolyline {
                    let indexes: [NSNumber] = options.lineIndexes.map { NSNumber(value: $0) }
                    let result = realPolyline.setPolylineWithCoordinates(&coords, count: UInt(coords.count), drawStyleIndexes: indexes)
                    if result {
                        //将内存中数据进行更新
                        polyline.update(options: options)
                    }
                }
            } else {
                if let realPolyline = polyline.realPolyline as? MAPolyline {
                    let result = realPolyline.setPolylineWithCoordinates(&coords, count: coords.count)
                    if result {
                        //将内存中数据进行更新
                        polyline.update(options: options)
                    }
                }
            }
            
        }
    }
    
    func exchange(polylines: Array<String>) {
        if polylines.count == 2, let first = polylines.first, let last = polylines.last {
            if let firstPolyline = innerPolylines.first { $0.options.identifier == first }, let lastPolyline = innerPolylines.first { $0.options.identifier == last } {
                if let realPolyline = firstPolyline.realPolyline as? MAPolyline, let lastRealPolyline = lastPolyline.realPolyline as? MAPolyline {
                    mapView.exchangeOverlay(realPolyline, with: lastRealPolyline)
                }
            }
        }
    }

    /// 通过polylineOptions.identifier移除已添加的polyline对象，
    func remove(polyline options: T3FPolylineOptions) -> Bool {
        let polylines = innerPolylines.filter { $0.options.identifier == options.identifier }
        for polyline in polylines {
            if let realPolyline = polyline.realPolyline as? MAPolyline {
                mapView.remove(realPolyline)
            }
            innerPolylines.removeAll { $0 == polyline }
        }
        return true
    }

    func show(polylines: [T3FPolylineOptions], edgePadding: UIEdgeInsets?, animated: Bool) {

        //遍历地图中polylines，找到需要进行展示的线段
        let targetPolylines = innerPolylines.filter { element in
            return polylines.contains { $0.identifier == element.options.identifier}
        }

        let result = targetPolylines.map { $0.realPolyline }

        guard !result.isEmpty else {
            T3Log.debug("高德地图-showPolylines，未找到需要展示的polylines")
            return
        }
        let closure = { [weak self] in
            T3Log.debug("高德地图-showPolylines")
            if let edgePadding = edgePadding {
                self?.mapView.showOverlays(result, edgePadding: edgePadding, animated: animated)
            } else {
                self?.mapView.showOverlays(result, animated: animated)
            }
        }

        if mapDidInit {
            closure()
        } else {
            T3Log.debug("高德地图-showPolylines-地图未初始化完成，暂存任务")
            showPolylineTask = closure
        }
    }

    // MARK: - func T3FAnnotation
    func add(annotations: [T3FAnnotationOptions]) {

        var realAnnotations = [T3FMARealAnnotation]()
        annotations.forEach { element in
            let realAnnotation = T3FMARealAnnotation(coordinate: element.coordinate)
            let annotation = T3FMAAnnotation(options: element, realAnnotation: realAnnotation)
            innerAnnotations.append(annotation)
            realAnnotations.append(realAnnotation)
        }

        mapView.addAnnotations(realAnnotations)
    }

    func update(annotation: T3FAnnotationOptions) {
        //根据id找到当前已绘制的ann
        let anns = innerAnnotations.filter { $0.options.identifier == annotation.identifier }
        for ann in anns {
            ann.update(options: annotation)
        }
        /// 高德地图在AnnotationView重绘时，如果只设置mapView重绘，则用于放置AnnotationView的mapView子视图containerView偶现未正常重绘，导致Annotation坐标位置未更新。
        /// 所以在下面手动调用mapView子视图的重绘来解决该问题。
        mapView.setNeedsLayout()
        mapView.layoutIfNeeded()

        mapView.subviews.forEach { view in
            view.setNeedsLayout()
            view.layoutIfNeeded()
        }
    }

    func remove(annotations: [T3FAnnotationOptions]) {

        var realAnnotations = [T3FMARealAnnotation]()
        annotations.forEach { element in
            let targetAnnotations = innerAnnotations.filter { $0.options.identifier == element.identifier }
            for annotation in targetAnnotations {
                innerAnnotations.removeAll { $0 == annotation }
                realAnnotations.append(annotation.realAnnotation)
            }
        }
        mapView.removeAnnotations(realAnnotations)
    }

    func show(annotations: [T3FAnnotationOptions], edgePadding: UIEdgeInsets?, animated: Bool) {
        //遍历地图中annotations，找到需要进行展示的标注
        let targetAnnotations = innerAnnotations.filter { element in
            return annotations.contains { $0.identifier == element.options.identifier}
        }

        let result = targetAnnotations.map { $0.realAnnotation }

        guard !result.isEmpty else {
            T3Log.debug("高德地图-showAnnotations，未找到需要展示的annotations")
            return
        }

        let closure = { [weak self] in
            T3Log.debug("高德地图-showAnnotations：\(edgePadding)")
            if let edgePadding = edgePadding {
                if result.count > 1 {
                    self?.mapView.showAnnotations(result, edgePadding: edgePadding, animated: true)
                } else if let annCoordinate = result.first?.coordinate, let targetCoordinate = self?.getMapCenterCoordinate(coordinate: annCoordinate, edgePadding: edgePadding) {
                    self?.mapView.setCenter(targetCoordinate, animated: true)
                }
            } else {
                self?.mapView.showAnnotations(result, animated: animated)
            }
        }

        if mapDidInit {
            closure()
        } else {
            T3Log.debug("高德地图-showPolylines-地图未初始化完成，暂存任务")
            showAnnotationTask = closure
        }

//        let resultAnnotationViews = result.compactMap { mapView.view(for:$0) }
//        let resultAnnotationViews1 = targetAnnotations.compactMap { $0.annotationView }
//
//        for ele in resultAnnotationViews1 {
//            let size = ele.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
//            print("ele.frame = \(size)")
//        }
//
//        let mapRects = resultAnnotationViews.compactMap { MAMapRectForCoordinateRegion(mapView.convert($0.frame, toRegionFrom: mapView)) }
//
//        if let first = mapRects.first {
//            let visibleRect = mapRects.reduce(first) { partialResult, next in
//                MAMapRectUnion(partialResult, next)
//            }
//
//            mapView.setVisibleMapRect(visibleRect, edgePadding: edgePadding ?? .zero, animated: animated)
//        }
    }
    
    func getMapCenterCoordinate(coordinate: CLLocationCoordinate2D, edgePadding: UIEdgeInsets) -> CLLocationCoordinate2D {
        //地图的视图锚点(跟图层锚点(anchor)不是同一个)
        let mapScreenAnchor = mapView.screenAnchor
        //地图锚点坐标
        
        let mapCenterPoint = CGPoint(x: mapView.frame.size.width * mapScreenAnchor.x,
                                     y: mapView.frame.size.height * mapScreenAnchor.y)
        //大头针坐标
        let waitAnnotationPoint = mapView.convert(coordinate, toPointTo: mapView)
        //大头针期望的坐标X
        let finalWaitAnnotationPointX = mapView.frame.size.width * 0.5
        //大头针期望的坐标Y
        let spaceHeight = mapView.frame.size.height - edgePadding.top - edgePadding.bottom
        let mapAvailableHeight = max(spaceHeight, 100)
        let finalWaitAnnotationPointY = mapAvailableHeight * 0.5 + edgePadding.top
        //偏移量
        let offsetX = waitAnnotationPoint.x - finalWaitAnnotationPointX
        let offsetY = waitAnnotationPoint.y - finalWaitAnnotationPointY
        //地图锚点新坐标
        let newMApCenterPoint = CGPoint(x: mapCenterPoint.x + offsetX, y: mapCenterPoint.y + offsetY)
        //地图锚点新经纬度
        let newCenterCoordinate = mapView.convert(newMApCenterPoint, toCoordinateFrom: mapView)
        return newCenterCoordinate
    }

    func show(polylines: [T3FPolylineOptions], annotations: [T3FAnnotationOptions], edgePadding: UIEdgeInsets?, animated: Bool) {

        //遍历地图中polylines，找到需要进行展示的线段
        let targetPolylines = innerPolylines.filter { element in
            return polylines.contains { $0.identifier == element.options.identifier}
        }

        let polylinesResult = targetPolylines.compactMap { ($0.realPolyline as? MAPolyline) }

        //遍历地图中annotations，找到需要进行展示的标注
        let targetAnnotations = innerAnnotations.filter { element in
            return annotations.contains { $0.identifier == element.options.identifier}
        }

        let annotationsResult = targetAnnotations.map { $0.realAnnotation }

        ///计算线段的占据矩形
        var lineMapRect: MAMapRect?
        if !polylinesResult.isEmpty {
            for polyline in polylinesResult {
                if let lastMapRect = lineMapRect {
                    lineMapRect = MAMapRectUnion(lastMapRect, polyline.boundingMapRect)
                } else {
                    lineMapRect = polyline.boundingMapRect
                }
            }
        }

        /// 计算标注物需要的矩形覆盖
        var annotationMapRect: MAMapRect?
        if !annotationsResult.isEmpty, let firstAnnotation = annotationsResult.first {
            let firstMapPoint = MAMapPointForCoordinate(firstAnnotation.coordinate)

            var minX: Double = firstMapPoint.x
            var minY: Double = firstMapPoint.y
            var maxX: Double = minX
            var maxY: Double = minY

            for ele in annotationsResult {
                let point = MAMapPointForCoordinate(ele.coordinate)

                if point.x < minX {
                    minX = point.x
                }
                if point.y < minY {
                    minY = point.y
                }
                if point.x > maxX {
                    maxX = point.x
                }
                if point.y > maxY {
                    maxY = point.y
                }
            }

            /// 必须加上最小值 
            let width: Double = max(abs(maxX - minX), 1)
            let height: Double = max(abs(maxY - minY), 1)

            annotationMapRect = MAMapRect(origin: MAMapPoint(x: minX, y: minY), size: MAMapSize(width: width, height: height))
        }

        if let lineMapRect = lineMapRect, let annotationMapRect = annotationMapRect {
            print("lineMapRect = \(lineMapRect)")
            print("annotationMapRect = \(annotationMapRect)")
            let resultRect = MAMapRectUnion(lineMapRect, annotationMapRect)
            print("resultRect = \(resultRect)")
            mapView.setVisibleMapRect(resultRect, edgePadding: edgePadding ?? .zero, animated: animated)
        } else if let lineMapRect = lineMapRect {
            mapView.setVisibleMapRect(lineMapRect, edgePadding: edgePadding ?? .zero, animated: animated)
        } else if let annotationMapRect = annotationMapRect {
            mapView.setVisibleMapRect(annotationMapRect, edgePadding: edgePadding ?? .zero, animated: animated)
        }
        else {
            T3Log.warning("无有效显示区域")
        }
    }

    func clear() {
        T3Log.debug("高德地图 clear")
        if let overlays = mapView.overlays {
            mapView.removeOverlays(overlays)
        }
        if let annotations = mapView.annotations {
            mapView.removeAnnotations(annotations)
        }
        innerPolylines.removeAll()
        innerAnnotations.removeAll()
        innerPolygons.removeAll()
    }
}

extension T3FMAMapView: MAMapViewDelegate {

    func mapViewDidFinishLoadingMap(_ mapView: MAMapView!) {
        delegate?.mapViewDidFinishLoadingMap(self)
    }

    /// 地图初始化完成
    func mapInitComplete(_ mapView: MAMapView!) {
        guard mapView != nil else { return }
        mapDidInit = true
        delegate?.mapInitComplete()
    }


    func mapViewRequireLocationAuth(_ locationManager: CLLocationManager!) {

        let authorizationStatus: CLAuthorizationStatus

        if #available(iOS 14.0, *) {
            authorizationStatus = locationManager.authorizationStatus
        } else {
            authorizationStatus = CLLocationManager.authorizationStatus()
        }

        if authorizationStatus == .notDetermined {

            locationManager.requestAlwaysAuthorization()
        }
    }

   

    /// 地图区域即将改变时会调用此接口
    func mapView(_ mapView: MAMapView!, regionWillChangeAnimated animated: Bool, wasUserAction: Bool) {
        delegate?.mapView(self, regionWillChangeAnimated: animated, wasUserAction: wasUserAction)
    }

    /// 地图区域改变过程中会调用此接口
    func mapViewRegionChanged(_ mapView: MAMapView!) {
        delegate?.mapViewRegionChanged(self, centerCoordinate: mapView.centerCoordinate, zoomLevel: mapView.zoomLevel)
    }

    /// 地图区域改变完成后会调用此接口
    func mapView(_ mapView: MAMapView!, regionDidChangeAnimated animated: Bool, wasUserAction: Bool) {
        delegate?.mapView(self, regionDidChangeAnimated: animated, wasUserAction: wasUserAction)
    }

    func mapView(_ mapView: MAMapView!, didSingleTappedAt coordinate: CLLocationCoordinate2D) {
        /// 用于处理点击polyline的事件处理
        if let result = mapView.getHittedPolylines(with: coordinate, traverseAll: false) as? [MAMultiTexturePolylineRenderer]  {
        
            guard let first = result.first else { return  }

            let findSelectedLine = innerPolylines.first { ele in
                if let mline = ele.realPolyline as? MAPolyline {
                    return mline == first.multiPolyline
                }
                else {
                    return false
                }
            }
            if let selectedLine = findSelectedLine {
                delegate?.mapView(self, didTapPolyline: selectedLine.options.identifier)
            }
        }
    }

    func mapView(_ mapView: MAMapView, rendererFor overlay: MAOverlay) -> MAOverlayRenderer? {
        /// 线段处理
        if let overlay = overlay as? T3RealPolylineProtocol {
            /// 校验图层覆盖物和内存变量数据一致性
            guard let result = innerPolylines.first(where: { $0.realPolyline == overlay }) else {
                T3Log.error("innerPolylines缓存中没有当前polyline-路线创建失败")
                return nil
            }

            let renderer: MAPolylineRenderer?
            switch result.options.lineType {
            case .multi:
                renderer = MAMultiTexturePolylineRenderer(from: result)
            case .normal:
                renderer = MAPolylineRenderer(with: result)
            }
            result.lineRender = renderer
            renderer?.userInteractionEnabled = true
            renderer?.hitTestInset = -2
            return renderer
        }

        return nil
    }

    func mapView(_ mapView: MAMapView!, viewFor annotation: MAAnnotation!) -> MAAnnotationView! {

        /// 添加标注
        if let annotation = annotation as? T3FMARealAnnotation {
            
            guard let result = innerAnnotations.first(where: { $0.realAnnotation == annotation }) else {
                T3Log.error("innerAnnotations缓存中没有当前annotation-标注创建失败")
                return nil
            }

            if let annotationView = T3FMAAnnotationView(annotation: annotation, reuseIdentifier: nil) {
                annotationView.zIndex = result.options.zIndex
                let contentView = result.annotationView
                annotationView.contentView = contentView

                /// 设置annotationView事件响应后的处理
                contentView.annotationDidClicked = { [weak self] (arguments) in
                    guard let bself = self else { return }
                    bself.delegate?.mapView(bself, didAnnotationViewTapped: result.options)
                }

                annotationView.addSubview(contentView)
                contentView.snp.remakeConstraints { make in
                    make.edges.equalToSuperview()
                }
                if let waitView = contentView as? T3FWaitAnnotationView {
                    if let annotationImage = waitView.options.image {
//                        annotationView.image = annotationImage
                        annotationView.centerOffset = CGPoint(x: 0, y: -annotationImage.size.height / 2)
                        annotationView.frame = CGRect(origin: .zero, size: annotationImage.size)
                    }
                    waitView.updateData()
                }
                return annotationView
            }
        }
        
        return nil
    }
    
    /// 用户实时位置
    func mapView(_ mapView: MAMapView!, didUpdate userLocation: MAUserLocation!, updatingLocation: Bool) {
        
        /// 更新实时位置的方向角
        if let heading = userLocation.heading {
            let degree = heading.trueHeading - Double(mapView.rotationDegree)
            let locationView = mapView.view(for: userLocation)
            locationView?.isEnabled = false
            locationView?.imageView?.transform = CGAffineTransform(rotationAngle: CGFloat(degree * Double.pi / 180))
        }
        
        if updatingLocation && userLocation != nil {
            if userLocation.location != nil {
                let location = T3FMapLocation(
                    provider: "iOS",
                    coordinate: userLocation.location.coordinate,
                    accuracy: userLocation.location.horizontalAccuracy,
                    altitude: userLocation.location.altitude,
                    bearing: userLocation.location.course,
                    speed: userLocation.location.speed,
                    time: userLocation.location.timestamp.timeIntervalSince1970)
                delegate?.userLocationDidUpdated(location: location)
            }
        }
    }
}

private enum T3FMAMapError: Error, CustomStringConvertible {

    case polylineAddFailed

    var description: String {
        switch self {
        case .polylineAddFailed:
            return "polyline添加失败"
        }
    }
}



